'use strict'

const playButton = document.getElementById('play')
const nameInput = document.getElementById('name')
const overlayDiv = document.getElementById('overlay')
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const colorToString = (rgb) => `rgb(${rgb.join(",")})`
const showNameRadio = document.getElementById('showName')
const darkThemeRadio = document.getElementById('darkTheme')
const scoreDiv = document.getElementById('score')
const chatBoxDiv = document.getElementById('cheatBox')
const chatTextDiv = document.getElementById('chatTextDiv')
const chatTextInput = document.getElementById('chatTextInput')


chatBoxDiv.hidden = true

let socket = io.connect("http://118.195.243.69:8888")

let isConnection = false
let isAlive = false

let receiveData = {
    view: {
        viewCenterX: 0,
        viewCenterY: 0
    },
    balls: [],
    totalMass: 0
}

let viewScale = 4

let darkTheme = true
let showName = true

var ping = 0
var lastRecvDataTime = 0
var lastUpdatePingTime = 0

var fps = 0
var lastUpdateFpsTime = 0

socket.on('connect', function() {
    // 连接成功后将按钮改为绿色
    isConnection = true
    playButton.style.backgroundColor = "green"
})

socket.on('heartbeat', function(data) {
    // 更新ping
    let currentTime = Date.now()
    if(currentTime - lastUpdatePingTime >= 1000) {
        ping = currentTime - lastRecvDataTime
        lastUpdatePingTime = currentTime
    }
    lastRecvDataTime = currentTime

    // 收到球球数据
    let temp = []
    // viewScale = data.view.viewScale + 4
    for(let i = 0; i < data.balls.length; i++) {
        let flag = false
        for(let j = 0; j < receiveData.balls.length; j++) {
            if(data.balls[i].id == receiveData.balls[j].id) {
                flag = true
                receiveData.balls[j].x = data.balls[i].x
                receiveData.balls[j].y = data.balls[i].y
                receiveData.balls[j].mass = data.balls[i].mass
                receiveData.balls[j].radius = data.balls[i].radius
                temp.push(receiveData.balls[j])
                break
            }
        }
        if(!flag) {
            temp.push(data.balls[i])
        }
    }
    receiveData.totalMass = Math.round(data.totalMass)
    receiveData.view = data.view
    receiveData.balls = temp
    
})

socket.on('message', function(data) {
    // 接收聊天消息
    console.log(data)
    chatTextDiv.innerHTML = data.name + ": " + data.msg + "<br>" + chatTextDiv.innerHTML
})

socket.on('started', function() {

    showName = showNameRadio.checked
    darkTheme = darkThemeRadio.checked

    console.log(showNameRadio.value )
    console.log(darkThemeRadio.value )
    console.log(showName)
    console.log(darkTheme)
    overlayDiv.style.display = "none"
    isAlive = true
    draw()
})

socket.on('dead', function() {
    isAlive = false
    overlayDiv.style.opacity = '1'
})

function setupEventHandlers(params) {
    // 开始游戏
    playButton.onclick = function() {
        if(!isConnection) {
            alert('未连接至服务器!')
            return;
        }
        let name = nameInput.value
        let color = Math.round(Math.random() * 65536)
        socket.emit('start', {
            name: name,
            color: color
        })
        isAlive = true
    }

    // 移动鼠标
    window.addEventListener('mousemove', (e) => {
        if(!isConnection || !isAlive) {
            return;
        }
        let data = screenToWorld(e.x, e.y)
        socket.emit('mousemove', data)
    })

    window.addEventListener('keydown', (e) => {
        // console.log(e.key)
        if(!isConnection || !isAlive) {
            return;
        }
        console.log(e.key)
        switch(e.key) {
            case 'q':
            case 'Q': 
                // 静止
                break;
            case 'w':
            case 'W': 
                // 吐球
                socket.emit('vomit', {vomit: true})
                break;
            case 'e':
            case 'E': 
                // 分身
                console.log('split')
                socket.emit('split', {split: true})
                break;
            case 'r':
            case 'R':
                // 重置
                window.location.reload()
                break;
            case 't':
            case 'T':
                // 打开/关闭聊天框
                chatBoxDiv.hidden = !chatBoxDiv.hidden
                break;
            case 'Enter':
                // 发送聊天
                socket.emit('message', {msg: chatTextInput.value})
                chatTextInput.value = ""
                break;
        }
    })

    window.addEventListener('keyup', (e) => {
        if(e.key == 'w' || e.key == 'W') {
            socket.emit('vomit', {vomit: false})
        }
    })

    window.addEventListener('resize', e => {
		canvas.width = window.innerWidth
		canvas.height = window.innerHeight
	})
    window.addEventListener('mousewheel', (e) => {
		// 滑动鼠标轮滑修改视野缩放
        if(e.wheelDelta > 0) {
            if(viewScale >= 10) {
                return
            }
			viewScale += 0.5
		} else {
            if(viewScale <= 1) {
                return
            }
			viewScale -= 0.5
		}
	})
}


function massToRadius(mass) {
    return Math.sqrt(mass / Math.PI)
}

function setup() {
    canvas.width = window.innerWidth
	canvas.height = window.innerHeight

    setupEventHandlers()
}


function screenToWorld(x, y) {
    return {
        x: receiveData.view.viewCenterX + (x - (canvas.width / 2)) / viewScale,
        y: receiveData.view.viewCenterY + (y - (canvas.height / 2)) / viewScale
    }
}

function worldToScreen(x, y) {
    return {
        x: (canvas.width / 2) + (x - receiveData.view.viewCenterX) * viewScale,
        y: (canvas.height / 2) + (y - receiveData.view.viewCenterY) * viewScale
    }
}

function drawGrid() {
	let gridSize = 50
    let view = receiveData.view
	ctx.beginPath()
	let xOff = (canvas.width / 2 - receiveData.view.viewCenterX * viewScale) % gridSize
	let yOff = (canvas.height / 2 - receiveData.view.viewCenterY * viewScale) % gridSize
	for (let i = 0; i < canvas.width / gridSize + 1; i++) {
		let x = i * gridSize + xOff
		ctx.moveTo(x, 0)
		ctx.lineTo(x, canvas.height)
	}
	for (let i = 0; i < canvas.height / gridSize + 1; i++) {
		let y = i * gridSize + yOff
		ctx.moveTo(0, y)
		ctx.lineTo(canvas.width, y)
	}
	ctx.lineWidth = 1

	ctx.strokeStyle = darkTheme ? `rgba(255, 255, 255, 0.15)` : `rgba(0, 0, 0, 0.15)`
	ctx.stroke()
}


function drawVirus(virus) {
	const spikeCount = Math.round(virus.radius * 1.2) * 6

	// prevent useless recalculations for every frame
	if (!virus._spikes || virus._lastRadius != virus.radius) {
		virus._spikes = []

		for (let i = 0; i <= spikeCount * 2; i += 1) {
			let a = i / spikeCount * Math.PI
			let extra = i % 2 === 0 ? -.5 : -.1

			virus._spikes.push({
				x: virus.x + (virus.radius + extra) * Math.cos(a),
				y: virus.y + (virus.radius + extra) * Math.sin(a)
			})
		}

		virus._lastRadius = virus.radius
	}

	ctx.beginPath()
	for (let {
			x,
			y
		} of virus._spikes) {
            let pos = worldToScreen(x, y)
		ctx.lineTo(pos.x, pos.y)
	}
	ctx.closePath()

	ctx.fillStyle = virus.color
	ctx.fill()

	ctx.lineWidth = 0.5 * viewScale
	ctx.strokeStyle = virus.color
	ctx.stroke()
}

function drawFood(food) {
	if (!food._vertices || food._lastRadius != food.radius) {
		const vertexCount = Math.floor(food.radius * 2.5) + 5

		food._vertices = []
		food._lastRadius = food.radius
		let rotation = Math.random() * Math.PI

		for (let i = 0; i <= vertexCount; i += 1) {
			let a = i / (vertexCount / 2) * Math.PI + rotation

			food._vertices.push({
				x: food.x + food.radius * Math.cos(a),
				y: food.y + food.radius * Math.sin(a)
			})
		}
	}

	ctx.beginPath()
	for (let {
			x,
			y
		} of food._vertices) {
			let pos = worldToScreen(x, y)
		ctx.lineTo(pos.x, pos.y)
	}
	ctx.closePath()

	ctx.fillStyle = food.color
	ctx.fill()
}


var lastDrawTime = 0
function draw() {

    let currentTime = Date.now()

    if(currentTime - lastUpdateFpsTime >= 1000) {
        fps = 1000 / (currentTime - lastDrawTime)
        lastUpdateFpsTime = currentTime
    }

    lastDrawTime = currentTime

    if(!isAlive) {
        return;
    }

	ctx.fillStyle = darkTheme ? "rgb(28, 28, 34)" : "rgb(255, 255, 255)"
	ctx.fillRect(0, 0, canvas.width, canvas.height)

    let view = receiveData.view
    
    let balls = receiveData.balls.sort((a, b) => {
		if(a.mass > b.mass) {
			return 1
		} else if(a.mass < b.mass) {
			return -1
		} else {
			return 0
		}
	})

    drawGrid()

    balls.forEach(ball => {
        // console.log(ball)
        if(ball.type == 'Food') {
            drawFood(ball)
        } else if(ball.type == 'Virus') {
            drawVirus(ball)
        } else {
            ctx.beginPath()
            let pos = {
                x: (canvas.width / 2) + (ball.x - view.viewCenterX) * viewScale,
                y: (canvas.height / 2) + (ball.y - view.viewCenterY) * viewScale
            }
            ctx.arc(pos.x, pos.y, ball.radius * viewScale, 0, Math.PI * 2)
            ctx.fillStyle = ball.color
            ctx.fill()
            if(showName && ball.type == 'Cell' && ball.name) {
                ctx.font = "14px Roboto"
                ctx.textAlign = "center"
                ctx.fillStyle = darkTheme ? "#fff" : "#000"
        
                if (true) {
                    ctx.fillText(ball.name, pos.x, pos.y + 8)
                }
            }
        }
    });

    scoreDiv.innerHTML = "总积分：" + receiveData.totalMass + "  ping: " + ping + "ms" + "  fps: " + Math.floor(fps)

    requestAnimationFrame(draw)
}





window.onload = function() {
    setup()
}